package com.atguigu.spring.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Order(1) // 多切片情况下数字越小优先级越高,越是外围,没有Order比较类名,靠前的在外围
@Component
@Aspect // 告诉Spring这个组件是个切面
public class LogAspect {
    /**
     * 告诉Spring通知何时何地运行
     * 时机：
     *  @Before: 方法执行前运行
     *  @AfterReturning: 方法执行正常返回结果运行
     *  @AfterThrowing: 方法抛出异常运行
     *  @After: 方法执行之后运行
     *  @Around: 环绕通知,上述方法的4in1,可以控制目标方法是否执行,可以修改目标方法的执行结果
     * 切入点:
     * 切入点表达式:
     *  &&、||、! 组合表达式
     *  execution(方法的全签名) 匹配方法执行连接点 eg: execution(* com.example.service.*.*(...))
     *      [public] int [com.atguigu.spring.aop.calculator.MathCalculator.]add(int, int) [throws Exception]
     *      省略写法: int add(int i, int j)
     *      通配符: * ①参数位置：表示多个参数,任意类型②类型位置代表多个层级
     *  within(type-pattern) 匹配指定类型内(包括子类型)的所有连接点
     *  this(type-pattern) 匹配代理对象是指定类型或其子类型的任何连接点
     *  target(type-pattern) 匹配目标对象是指定类型或其子类型的任何连接点 eg: target(com.example.MyBean)
     *  args(param-pattern) 匹配方法参数是指定类型或其子类型的任何连接点 eg: args(java.io.Serializable)
     *  bean(bean-id-or-name-pattern) 匹配特定spring bean的所有连接点 依赖Spring Bean的名称或id
     *  @target(annotation-type) 匹配标注了指定注解的所有目标对象的方法 eg: @target(org.springframework.transaction.annotation.Transactional)
     *  @args(annotation-type) 匹配方法参数标注指定注解 eg: @args(com.atguigu.Hello)
     *  @within(annotation-type) 匹配目标对象类型上拥有指定注解的所有方法 eg: @within(org.springframework.transaction.annotation.Transactional)
     *  @annotation(annotation-type) 匹配任何被指定注解标注的方法 eg: @annotation(org.springframework.transaction.annotation.Transactional)
     *
     *
     *  增强器链 切面中的所有通知方法其实就是增强器  他们被组织成一个链路放到集合中 目标方法触发增强器链中的方法执行
     *
     *  通知方法的执行顺序
     *      正常链路： 前置通知->目标方法->返回通知->后置通知
     *      异常链路： 前置通知->目标方法->异常通知->后置通知
     *
     *
     * JoinPoint 包装了当前目标方法的所有信息
     */
    @Pointcut("execution(int com.atguigu.spring.aop.calculator.MathCalculator.*(int, int))")
    public void pointCut() {}

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getMethod().getName();
        String paramTypes = Arrays.toString(methodSignature.getParameterTypes());
        String params = Arrays.toString(joinPoint.getArgs());
        System.out.println("【切面 - 日志】：" + methodName + paramTypes + "开始, 参数：" + params);
    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        System.out.println("【切面 - 日志】：" + methodSignature + "结束");
    }

    @AfterReturning(value = "pointCut()",
            returning = "result")
    public void logResult(JoinPoint joinPoint, Object result) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        System.out.println("【切面 - 日志】：" + methodSignature + "结果：" + result);
    }

    @AfterThrowing(value = "pointCut()",
            throwing = "e")
    public void logException(JoinPoint joinPoint, Throwable e) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        System.out.println("【切面 - 日志】：" + methodSignature + "异常:"+e.getMessage());
    }

//    @Before("args(int, int)")
    public void logSpecialArg() {

        System.out.println("【切面 - 日志】：指定参数");
    }
}
